#include "rs/Semaphore.hpp"
#include "rs/Assert.hpp"
#include "rs/portability/platforms/posix/Implementations.hpp"
#include <cstdio>

namespace rs
{
    Semaphore::Semaphore(UInt32 value)
    : implementation_(new internal::SemaphoreImplementation) {
#   if RS_PLATFORM == RS_PLATFORM_MACOSX
        kern_return_t err = semaphore_create(mach_task_self(), &implementation_->semaphoreHandle_, SYNC_POLICY_FIFO, value);
        RS_ASSERT(err == KERN_SUCCESS, "Creating mach semaphore failed");
#   else
        RS_ASSERT(value <= SEM_VALUE_MAX, "Semaphore's initial value is too large");
        int err = sem_init(&implementation_->semaphoreHandle_, 0, value);
        RS_ASSERT(err != -1, "Initializing posix semaphore failed");
#   endif
    }
    
    Semaphore::Semaphore(Semaphore const& other)
    : implementation_(other.implementation_) {
    }
    
    Semaphore::~Semaphore() {
#   if RS_PLATFORM == RS_PLATFORM_MACOSX
        kern_return_t err = semaphore_destroy(mach_task_self(), implementation_->semaphoreHandle_);
        RS_ASSERT(err == KERN_SUCCESS, "Destroying mach semaphore failed");
#   else
        int err = sem_destroy(&implementation_->semaphoreHandle_);
        RS_ASSERT(err == 0, "Destroying posix semaphore failed");
#   endif
    }
    
    void Semaphore::Increment() {
#   if RS_PLATFORM == RS_PLATFORM_MACOSX
        kern_return_t err = semaphore_signal(implementation_->semaphoreHandle_);
        RS_ASSERT(err == KERN_SUCCESS, "Incrementing/signaling mach semaphore failed");
#   else
        int err = sem_post(&implementation_->semaphoreHandle_);
        RS_ASSERT(err == 0, "Incrementing/posting/signaling posix semaphore failed");
#   endif
    }
    
    void Semaphore::Decrement() {
#   if RS_PLATFORM == RS_PLATFORM_MACOSX
        kern_return_t err = semaphore_wait(implementation_->semaphoreHandle_);
        RS_ASSERT(err == KERN_SUCCESS, "Incrementing/signaling mach semaphore failed");
#   else
        int err = sem_wait(&implementation_->semaphoreHandle_);
        RS_ASSERT(err == 0, "Decrementing/waiting posix semaphore failed");
#   endif
    }
    
    void Semaphore::Swap(Semaphore& other) {
        std::swap(implementation_, other.implementation_);
    }
    
    Semaphore& Semaphore::operator=(Semaphore const& other) {
        if (this != &other) {
            Semaphore(other).Swap(*this);
        }
        
        return *this;
    }
}